

import json
import re
from dataclasses import dataclass
from typing import List, Optional
from unidiff import PatchSet
from clients import GerritClient
from config import BLOCK_LINE_SIZE, MAX_DETECT_LINES_PER_FILE, CODE_HEADER_FILE_EXTENSIONS, CODE_FILE_EXTENSIONS, HEADER_FILE_DETECT_LINES


@dataclass
class FunctionInfo:
    """函数信息类"""
    name: str
    start_line: int
    end_line: int
    return_type: str
    parameters: str

    def __getitem__(self, index: int) -> any:
        """支持索引访问，按定义顺序"""
        fields = [self.name, self.start_line, self.end_line, self.return_type, self.parameters]
        return fields[index]


class CodeChangeParser:
    def __init__(self):
        # 改进的函数定义正则表达式，支持更多情况
        self.function_patterns = [
            # 标准函数定义: 返回类型 函数名(参数) {
            re.compile(r'^\s*((?:[\w\*]+\s+)+)(\w+)\s*\(([^)]*)\)\s*\{?\s*$'),
            # 支持static/inline等修饰符
            re.compile(r'^\s*(?:static|inline|extern|virtual)\s+((?:[\w\*]+\s+)+)(\w+)\s*\(([^)]*)\)\s*\{?\s*$'),
            # 支持类成员函数: 返回类型 类名::函数名(参数) {
            re.compile(r'^\s*((?:[\w\*]+\s+)+)(\w+::\w+)\s*\(([^)]*)\)\s*\{?\s*$'),
        ]

    def _find_function_by_line(self, functions: List, target_line: int) -> Optional[FunctionInfo]:
        """
        查找指定行号所在的函数

        Args:
            file_path: 文件路径
            target_line: 目标行号

        Returns:
            函数信息, 如果不在任何函数中则返回None
        """
        if functions is None or len(functions) == 0:
            return None
        try:

            for func in functions:
                if func.start_line <= target_line:
                    if func.end_line == 0:  # 未结束的函数
                        return func
                    elif target_line <= func.end_line:
                        return func

            return None
        except Exception as e:
            print(f"查找函数时出错: {e}")
            return None

    def _match_function_definition(self, line: str) -> Optional[tuple]:
        """匹配函数定义"""
        for pattern in self.function_patterns:
            match = pattern.match(line)
            if match:
                return_type = match.group(1)
                function_name = match.group(2)
                parameters = match.group(3) if pattern.groups >= 3 else ""
                return return_type, function_name, parameters
        return None

    def _parse_functions(self, file_content: List[str]) -> List[FunctionInfo]:
        """
        解析文件中的所有函数

        Args:
            file_path: 文件路径

        Returns:
            函数信息列表
        """
        functions = []
        brace_count = 0
        current_function = None

        for line_num, line in enumerate(file_content, 1):
            # 保留原始行用于调试，但处理时去除首尾空白
            original_line = line
            line = line.strip()

            # 跳过空行
            if not line:
                continue

            # 跳过预处理指令（但不跳过包含函数定义的预处理指令）
            if line.startswith('#') and not any(pattern.search(line) for pattern in self.function_patterns):
                continue

            # 如果不在函数中，检查是否是函数定义
            if brace_count == 0 and current_function is None:
                function_match = self._match_function_definition(line)
                if function_match:
                    return_type, function_name, parameters = function_match

                    current_function = FunctionInfo(
                        name=function_name,
                        start_line=line_num,
                        end_line=0,
                        return_type=return_type.strip(),
                        parameters=parameters.strip()
                    )

                    # 如果这行包含开括号，开始计数
                    if '{' in original_line:
                        brace_count = 1
                    continue

            # 如果在函数中，统计大括号
            if current_function is not None:
                # 统计大括号（只统计不在注释和字符串中的）
                in_string = False
                in_comment = False
                escape_next = False
                string_char = None

                for char in original_line:
                    if escape_next:
                        escape_next = False
                        continue

                    if char == '\\':
                        escape_next = True
                        continue

                    if not in_comment and not in_string and char == '/':
                        # 可能开始注释
                        continue

                    if not in_comment and char in ['"', "'"]:
                        if not in_string:
                            in_string = True
                            string_char = char
                        elif string_char == char:
                            in_string = False
                            string_char = None
                        continue

                    if not in_string and not in_comment:
                        if char == '{':
                            brace_count += 1
                        elif char == '}':
                            brace_count -= 1

                            # 如果大括号计数归零，函数结束
                            if brace_count == 0:
                                current_function.end_line = line_num
                                functions.append(current_function)
                                current_function = None
                                break

        # 处理没有正确结束的函数
        if current_function is not None:
            current_function.end_line = len(file_content)
            functions.append(current_function)

        return functions

    def parse_code_change(self, pch: str, change_id: int|str, gerrit_client:GerritClient):
        patchset = PatchSet(pch)
        req_list = list()
        single_req = dict()
        for file in patchset:
            # >5000行跳过检测
            if file.added > MAX_DETECT_LINES_PER_FILE :
                continue
            # 只检测C/CPP系列文件
            if not file.path.endswith(CODE_FILE_EXTENSIONS):
                continue
            file_content = gerrit_client.get_file_content(change_id, file.path)
            # 如果是新增头文件，最多只检测前500行
            if file.path.endswith(CODE_HEADER_FILE_EXTENSIONS) and file.is_added_file:
                chunk = file_content[0:BLOCK_LINE_SIZE]
                for i in range(len(chunk)):
                    line = chunk[0]
                    chunk.pop(0)
                    chunk.append(str(i+1) + line)
                block = ''.join(chunk)
                single_req[file.path] = block
                req_list.append((json.dumps(single_req), True))
                single_req = {}
                continue
            changes = list()
            
            # 获取变更区间
            for hunk in file:
                changes.append((hunk.target_start, hunk.target_start + hunk.target_length - 1))
            # 解析源文件函数区间
            functions = self._parse_functions(file_content)
            # 提取变更变更所在函数，分块大小为500行
            blocks = self._extract_and_chunk_functions(changes, functions, file_content)

            for block in blocks:
                single_req[file.path] = block
                req_list.append((json.dumps(single_req), False))
                single_req = {}
        return req_list

    def _extract_and_chunk_functions(self, changes: List[tuple[int, int]], functions: List[FunctionInfo], source_lines: List[str], max_lines: int = BLOCK_LINE_SIZE) -> List[dict]:
        """
        提取与变更区间重叠的函数,并按500行切分为字符串列表
        
        前置条件：
        - changes 按起始行升序排列
        - functions 按起始行升序排列
        
        Returns:
            List[str]: 每个元素是一个块的完整内容字符串
        """
        if not changes or not source_lines:
            return []
        
        # 1. 有序去重提取（双指针，O(M+N)）
        affected_funcs  = []
        seen_functions = set()
        
        func_idx = 0
        for cs, ce in changes:
            # 跳过 func_end < change_start 的函数
            while func_idx < len(functions) and functions[func_idx][2] < cs:
                func_idx += 1
            
            # 检查重叠
            check_idx = func_idx
            while check_idx < len(functions) and functions[check_idx][1] <= ce:
                function = functions[check_idx]
                func_key = (function[0], function[1], function[2])
                if func_key not in seen_functions:
                    seen_functions.add(func_key)
                    affected_funcs.append(func_key)
                check_idx += 1
        
        # 2. 贪心分块（仅累积内容字符串）
        blocks: List[str] = []
        current_content: List[str] = []  # 累积当前块的行
        current_lines = 0

        # 特殊处理前200行是否存在非函数变更
        flag = False
        end_line = 0
        for f_start, f_end in changes:
            if f_start < HEADER_FILE_DETECT_LINES and f_end <= HEADER_FILE_DETECT_LINES:
                for line in range(f_start, f_end+1):
                    function = self._find_function_by_line(functions, line)
                    if function is None:
                        flag = True
                    else:
                        if function[1] <= HEADER_FILE_DETECT_LINES: 
                            end_line = function[1] - 1
        if flag:
            if end_line == 0: 
                end_line = HEADER_FILE_DETECT_LINES
            for i, line in enumerate(source_lines[0:end_line]):
                current_content.append(str(i+1) + line)  
        current_lines = len(current_content)

        for _, f_start, f_end in affected_funcs:
            if flag and f_start < end_line:
                continue
            func_lines = []
            for i, line in enumerate(source_lines[f_start-1:f_end]):
                func_lines.append(str(i+f_start) + line)
            func_line_count = len(func_lines)
            
            # 情况A：函数本身超过max_lines → 强制拆分
            if func_line_count > max_lines:
                # 保存当前块
                if current_content:
                    blocks.append(''.join(current_content))
                    current_content, current_lines = [], 0
                
                # 直接添加拆分后的块
                for i in range(0, func_line_count, max_lines):
                    blocks.append(''.join(func_lines[i:i+max_lines]))
            
            # 情况B：加入当前块会超额 → 新开一块
            elif current_lines + func_line_count > max_lines:
                blocks.append(''.join(current_content))
                current_content, current_lines = func_lines, func_line_count
            
            # 情况C：正常加入
            else:
                current_content.extend(func_lines)
                current_lines += func_line_count
        
        # 保存最后一个块
        if current_content:
            blocks.append(''.join(current_content))
        
        return blocks
